<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Language" content="zh-CN"><link href="stylesheet.css" media="all" rel="stylesheet" type="text/css">
<title>应用层数据完整性检查</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head><body class="SECT1">
<div>
<table summary="Header navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><th colspan="5" align="center" valign="bottom">PostgreSQL 8.2.3 中文文档</th></tr>
<tr><td width="10%" align="left" valign="top"><a href="explicit-locking.html" accesskey="P">后退</a></td><td width="10%" align="left" valign="top"><a href="mvcc.html">快退</a></td><td width="60%" align="center" valign="bottom">章12. 并发控制</td><td width="10%" align="right" valign="top"><a href="mvcc.html">快进</a></td><td width="10%" align="right" valign="top"><a href="locking-indexes.html" accesskey="N">前进</a></td></tr>
</table>
<hr align="LEFT" width="100%"></div>
<div class="SECT1"><h1 class="SECT1"><a name="APPLEVEL-CONSISTENCY">12.4. 应用层数据完整性检查</a></h1>
<p>因为不管哪种隔离级别，对 PostgreSQL 的读动作都不会锁定数据，所以一个事务读取的数据可能被另一个事务覆盖。换句话说，如果一条 <tt class="COMMAND">SELECT</tt> 返回了一行，这并不意味着在返回该行时该行还存在，也就是说在语句完成或事务开始后的某时该行可能已经被一个在此事务开始之后提交的事务更新或者删除。即使该行"现在"仍然有效，它也可能在当前事务提交或者回滚之前被改变或者删除。</p>
<p>另外一个认识它的方法是每个事务都看到一个数据库内容的快照，而并发执行的事务很可能看到不同的快照。因此不管怎样，整个"现在"的概念都是含混不清的。不过如果客户端应用相互隔离，那么这就不是个大问题，但是如果客户端之间在数据库外部相互通讯，那就可能有严重的歧义。</p>
<p>要保证一行当前实际存在和避免其被同时更新，我们必须使用 <tt class="COMMAND">SELECT FOR UPDATE</tt>, <tt class="COMMAND">SELECT FOR SHARE</tt> 或者合适的 <tt class="COMMAND">LOCK TABLE</tt> 语句。<tt class="COMMAND">SELECT FOR UPDATE</tt> 或 <tt class="COMMAND">SELECT FOR SHARE</tt> 只是对其它的并发更新锁住返回的行，而 <tt class="COMMAND">LOCK TABLE</tt> 保护整个表。当从其它环境向 PostgreSQL 里用可串行化模式移植应用时一定要把这些问题考虑进去。</p>
<p>在 MVCC 环境下，全局有效性检查需要一些额外的考虑。比如，一个银行应用可能会希望检查一个表中的所有扣款总和等于另外一个表中的加款总和，同时两个表还会被活跃地更新。在读已提交模式下比较两个连续的 <tt class="LITERAL">SELECT sum(...)</tt> 命令的结果是不可靠的，因为第二个查询很可能会包含第一个没计算的事务提交的结果。在一个可串行化的事务里进行两个求和则给出在可串行化事务开始之前提交的所有事务产生的精确的结果，但我们还是会合理地置疑在结果提交的时候，它们是否还相关。如果可串行化事务本身在试图做一致性检查之前进行了某些变更，那么检查的有用性就更加值得讨论了，因为现在它包含了一些(但不是全部)事务开始后的变化。在这种情况下，一个仔细的人会希望锁住所有需要检查的表，这样才能获得一个无可置疑的当前现状的图像。一个 <tt class="LITERAL">SHARE</tt> 模式(或者更高级)的锁保证在被锁定表中除了当前事务之外，没有未提交的更新。</p>
<p>还要注意如果我们依赖明确锁定来避免并发更新，那么我们应该使用读已提交模式，或者是在可串行化模式里在执行命令之前小心地获取锁。在可串行化事务里获取的锁保证了不会有其它正在运行的修改该表的事务存在，但是如果事务看到的快照提前获取了锁，那么它可能提前把一些现在已经提交的改变放到表中。一个可串行化事务的快照实际上是在它的第一个查询或者数据修改命令(<tt class="LITERAL">SELECT</tt>, <tt class="LITERAL">INSERT</tt>, <tt class="LITERAL">UPDATE</tt>, <tt class="LITERAL">DELETE</tt>)开始的时候冻结的，因此我们可以在快照冻结之前明确获取锁。</p>
</div>
<div>
<hr align="LEFT" width="100%">
<table summary="Footer navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td width="33%" align="left" valign="top"><a href="explicit-locking.html" accesskey="P">后退</a></td><td width="34%" align="center" valign="top"><a href="index.html" accesskey="H">首页</a></td><td width="33%" align="right" valign="top"><a href="locking-indexes.html" accesskey="N">前进</a></td></tr>
<tr><td width="33%" align="left" valign="top">明确锁定</td><td width="34%" align="center" valign="top"><a href="mvcc.html" accesskey="U">上一级</a></td><td width="33%" align="right" valign="top">锁和索引</td></tr>
</table>
</div>
</body></html>